package parser;

import scanner.Scanner;
import scanner.Token;
import scanner.TokenId;

public class ExpressionParser
    extends BaseParser
{
    public ExpressionParser(Scanner scanner, Token currentToken)
    {
        super(scanner);
        this.currentToken = currentToken;
    }

    public SyntaxNode Parse()
    {
        return ParseExpression();
    }

    private SyntaxNode ParseExpression()
    {
        SyntaxNode expressionNode = new SyntaxNode(SyntaxNodeType.EXPRESSION);
        expressionNode.AddChild(ParseComparisonExpression());
        if (IsExpressionOpt())
        {
            expressionNode.AddChild(ParseExpressionOpt());
        }
        return expressionNode;
    }

    private SyntaxNode ParseExpressionOpt()
    {
        SyntaxNode exprOpt = null;
        if (currentToken.GetId() == TokenId.AND)
        {
            exprOpt = new SyntaxNode(SyntaxNodeType.LOGICAL_AND);
            ReadToken();
            exprOpt.AddChild(ParseComparisonExpression());
            if (IsExpressionOpt())
            {
                exprOpt.AddChild(ParseExpressionOpt());
            }
        }
        return exprOpt;
    }

    private SyntaxNode ParseComparisonExpression()
    {
        SyntaxNode comparisonExpressionNode = new SyntaxNode(SyntaxNodeType.COMPARISON_EXPRESSION);
        comparisonExpressionNode.AddChild(ParseArithmeticExpression());
        if (IsComparisonExpressionOpt())
        {
            comparisonExpressionNode.AddChild(ParseComparisonExpressionOpt());
        }
        return comparisonExpressionNode;
    }

    private SyntaxNode ParseComparisonExpressionOpt()
    {
        SyntaxNode exprOpt = null;
        if (currentToken.GetId() == TokenId.LESS)
        {
            exprOpt = new SyntaxNode(SyntaxNodeType.LESS_THAN);
            ReadToken();
            exprOpt.AddChild(ParseArithmeticExpression());
            if (IsComparisonExpressionOpt())
            {
                exprOpt.AddChild(ParseComparisonExpressionOpt());
            }
        }
        return exprOpt;
    }

    private SyntaxNode ParseArithmeticExpression()
    {
        SyntaxNode arithmeticExpressionNode = new SyntaxNode(SyntaxNodeType.ARITHMETIC_EXPRESSION);
        arithmeticExpressionNode.AddChild(ParseTerm());
        if (IsArithmeticExpressionOpt())
        {
            arithmeticExpressionNode.AddChild(ParseArithmeticExpressionOpt());
        }
        return arithmeticExpressionNode;
    }

    private SyntaxNode ParseArithmeticExpressionOpt()
    {
        SyntaxNode exprOpt = null;

        switch (currentToken.GetId())
        {
            case PLUS:
                exprOpt = new SyntaxNode(SyntaxNodeType.ADD);
                break;
            case MINUS:
                exprOpt = new SyntaxNode(SyntaxNodeType.SUB);
                break;
        }
        if (exprOpt != null)
        {
            ReadToken();
            exprOpt.AddChild(ParseTerm());
            if (IsArithmeticExpressionOpt())
            {
                exprOpt.AddChild(ParseArithmeticExpressionOpt());
            }
        }
        return exprOpt;
    }

    private SyntaxNode ParseTerm()
    {
        SyntaxNode termNode = new SyntaxNode(SyntaxNodeType.TERM);
        termNode.AddChild(ParseFactor());
        if (IsTermOpt())
        {
            termNode.AddChild(ParseTermOpt());
        }
        return termNode;
    }

    private SyntaxNode ParseTermOpt()
    {
        SyntaxNode termOpt = null;
        if (currentToken.GetId() == TokenId.MUL)
        {
            termOpt = new SyntaxNode(SyntaxNodeType.MUL);
            ReadToken();
            termOpt.AddChild(ParseFactor());
            if (IsTermOpt())
            {
                termOpt.AddChild(ParseTermOpt());
            }
        }
        return termOpt;
    }

    private SyntaxNode ParseFactor()
    {
        SyntaxNode factorNode = null;
        switch (currentToken.GetId())
        {
            case INTEGER_LITERAL:
                factorNode = new SyntaxNode(SyntaxNodeType.INT_VAL, currentToken.GetData());
                break;
            case IDENTIFIER:
                factorNode = ParseIdentifier();
                break;
            case TRUE:
            case FALSE:
                factorNode = new SyntaxNode(SyntaxNodeType.BOOLEAN_VALUE, currentToken.GetData());
                break;
            case EXCLAMATION:
                factorNode = new SyntaxNode(SyntaxNodeType.LOGICAL_NOT);
                ReadToken();
                factorNode.AddChild(ParseExpression());
                break;
            case LEFT_ROUND_BRACKET:
                factorNode = new SyntaxNode(SyntaxNodeType.EXPR_IN_ROUND_BRACKETS);
                ReadToken();
                factorNode.AddChild(ParseExpression());
                if (currentToken.GetId() != TokenId.RIGHT_ROUND_BRACKET)
                {
                    HandleError(") expected");
                }
                ReadToken();
                break;
            default:
                HandleError("Expression expected");
                break;
        }
        return factorNode;
    }

    private boolean IsComparisonExpressionOpt()
    {
        return IsTokenNext(TokenId.LESS);
    }

    private boolean IsArithmeticExpressionOpt()
    {
        return IsTokenNext(TokenId.PLUS, TokenId.MINUS);
    }

    private boolean IsExpressionOpt()
    {
        return IsTokenNext(TokenId.AND);
    }

    private boolean IsTermOpt()
    {
        return IsTokenNext(TokenId.MUL);
    }

}
